package com.future.common.utils.async;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.function.Supplier;

import org.apache.log4j.MDC;

import com.future.common.global.PlatFormContext;

/**
 * 
 * @author Leon.Zhang 异步执行方法工具类
 */
public class AsyncUtil {
	private static ExecutorService defaultExecutorService = Executors.newCachedThreadPool();

	public static void setDefaultExecutorService(ExecutorService executorService) {
		defaultExecutorService = executorService;
	}

	public static <T> Future<T> get(Supplier<T> supplier) {
		return execute(supplier);
	}

	public static <T> Future<T> get(Supplier<T> supplier, ExecutorService executorService) {
		return execute(supplier, executorService);
	}

	public static void process(VoidFunctionTask task) {
		execute(task);
	}

	public static void process(VoidFunctionTask task, ExecutorService executorService) {
		execute(task, executorService);
	}

	private static <T> Future<T> execute(Supplier<T> supplier) {
		ContextBean contextBean = generateContextBean();
		Callable<T> callback = () -> {
			setContext(contextBean);
			T result;
			try {
				result = supplier.get();
			} finally {
				removeContext();
			}
			return result;
		};
		return defaultExecutorService.submit(callback);
	}

	private static <T> Future<T> execute(Supplier<T> supplier, ExecutorService executorService) {
		ContextBean contextBean = generateContextBean();
		Callable<T> callback = () -> {
			setContext(contextBean);
			T result;
			try {
				result = supplier.get();
			} finally {
				removeContext();
			}
			return result;
		};
		return executorService.submit(callback);
	}

	private static void execute(VoidFunctionTask task) {
		ContextBean contextBean = generateContextBean();
		Runnable runnable = () -> {
			setContext(contextBean);
			try {
				task.run();
			} finally {
				removeContext();
			}
		};
		defaultExecutorService.submit(runnable);
	}

	private static void execute(VoidFunctionTask task, ExecutorService executorService) {
		ContextBean contextBean = generateContextBean();
		Runnable runnable = () -> {
			setContext(contextBean);
			try {
				task.run();
			} finally {
				removeContext();
			}
		};
		executorService.submit(runnable);
	}

	private static ContextBean generateContextBean() {
		ContextBean contextBean = new ContextBean();
		contextBean.setRequestId((String) MDC.get("REQUESTID"));
		contextBean.setCurrentUser(PlatFormContext.getCurrentUser());
		contextBean.setFromMobile(PlatFormContext.getFromMobile());
		return contextBean;
	}

	private static void setContext(ContextBean contextBean) {
		PlatFormContext.setCurrentUser(contextBean.getCurrentUser());
		PlatFormContext.setFromMobile(contextBean.getFromMobile());
		MDC.put("REQUESTID", contextBean.getRequestId());
	}

	private static void removeContext() {
		PlatFormContext.removeCurrentUser();
		PlatFormContext.removeFromMobile();
		MDC.clear();
	}

}
